
#include <BFont>
#include <BStyle>
#include "member_BTableWidget.h"
#include "member_BStyle.h"
#include "member_BWidget.h"

using namespace BWE;

member_BTableWidget::member_BTableWidget(BTableWidget* tableWidget)
{
	boss = tableWidget;
	gridStyle = Grid_None;
	gridSize = 1;
	autoTop = true;
	autoLeft = true;

	selectMode = Select_Multiple;
	selecting = false;
	pressedItem = 0;
	hoveredItem = 0;
	dirtyItems = false;
	columnHeader.orientation = Orientation_Horizontal;
	rowHeader.orientation = Orientation_Vertical;
}
member_BTableWidget::~member_BTableWidget()
{

}

BTableItem* member_BTableWidget::itemAtPoint(const BPoint& pos)
{
	if (items.empty())
		return 0;

	const BRect& crect = boss->centerRect();
	if (!crect.contain(pos))
		return 0;

	int row = -1;

	for (int r = rowHeader.show_begin; r < rowHeader.show_end; r++)
	{
		int top = rowHeader.headers[r].rect.top();
		int bottom = rowHeader.headers[r].rect.bottom();
		if (pos.y() >= top && pos.y() <= bottom)
		{
			row = r;
			break;
		}
	}

	int column = -1;
	for (int c = columnHeader.show_begin; c < columnHeader.show_end; c++)
	{
		int left = columnHeader.headers[c].rect.left();
		int right = columnHeader.headers[c].rect.right();
		if (pos.x() >= left && pos.x() <= right)
		{
			column = c;
			break;
		}
	}

	if (row >= 0 && column >= 0)
	{
		return items[row][column];
	}
	return 0;
}

void member_BTableWidget::dirtyRowItems(int row)
{
	rowHeader.headers[row].dirtyItems = true;
	for (int c = 0; c < items.column(); c++)
	{
		if (BTableItem* item = items[row][c])
		{
			item_member(item)->dirty = true;
			IHeader& header = columnHeader.headers[c];
			if (header.policy & Policy_Preferred)
			{
				header.dirty = true;
				header.dirtyItems = true;
			}
		}
	}
	dirtyItems = true;
}
void member_BTableWidget::dirtyColumnItems(int column)
{
	columnHeader.headers[column].dirtyItems = true;
	for (int r = 0; r < items.row(); r++)
	{
		if (BTableItem* item = items[r][column])
		{
			item_member(item)->dirty = true;
			IHeader& header = rowHeader.headers[r];
			if (header.policy & Policy_Preferred)
			{
				header.dirty = true;
				header.dirtyItems = true;
			}
		}
	}
	dirtyItems = true;
}

void member_BTableWidget::freshColumnHeader(const BStyle* style)
{
	const BFont* font = 0;
	if (columnHeader.style)
		font = columnHeader.style->value(Value_Font);
	if (!font)
		font = style->font();

	const BFont* itemFont = style->font();

	columnHeader.resizable = false;
	int hintWidth = columnHeader.spacing;
	int hintHeight = columnHeader.baseSize.height() + columnHeader.margin.height();
	int realWidth = columnHeader.spacing;
	int realHeight = columnHeader.baseSize.height();
	for (int c = 0; c < columnHeader.headers.size(); c++)
	{
		IHeader& header = columnHeader.headers[c];
		if (!header.visible)
			continue;
		if (header.dirtyItems && header.policy & Policy_Preferred)
		{
			if (!(header.policy & Policy_Fixed))
				header.realSize.width() = 0;
			header.itemsSize.reset();
			for (int r = 0; r < items.row(); r++)
			{
				BTableItem* item = items[r][c];
				if (!item)
					continue;
				if (item_member(item)->dirtyStyle)
				{
					item_member(item)->dispenseStyles(style);
					item_member(item)->applyStyle(style);
					item_member(item)->dirtyStyle = false;
					item_member(item)->dirty = true;
				}
				if (item_member(item)->dirty)
				{
					item_member(item)->freshSize(itemFont, 0);
					item_member(item)->dirty = false;
				}
				int itemWidth = item_member(item)->hintSize.width();
				if (header.itemsSize.width() < itemWidth)
					header.itemsSize.width() = itemWidth;
			}
			header.dirtyItems = false;
		}
		if (header.dirty)
		{
			header.freshSize(columnHeader.style, font);
			header.hintSize += columnHeader.margin.size();
			header.hintSize.limit(columnHeader.minSize, columnHeader.maxSize);
			header.rect.setWidth(0);
			header.dirty = false;
		}
		header.realSize.reset();
		if (header.policy & Policy_Preferred && header.realSize.width() < header.hintSize.width())
			header.realSize.width() = header.hintSize.width();
		if (header.policy & Policy_Fixed && header.realSize.width() < header.fixedSize.width())
			header.realSize.width() = header.fixedSize.width();
		if (header.policy & Policy_Dynamic)
			columnHeader.resizable = true;
		hintWidth += header.hintSize.width() + columnHeader.spacing;
		if (hintHeight < header.hintSize.height())
			hintHeight = header.hintSize.height();
		realWidth += header.realSize.width() + columnHeader.spacing;
		if (realHeight < header.realSize.height())
			realHeight = header.realSize.height();
	}
	columnHeader.hintSize.set(hintWidth, hintHeight);
	columnHeader.realSize.set(realWidth, realHeight);
}
void member_BTableWidget::freshRowHeader(const BStyle* style)
{
	const BFont* font = 0;
	if (rowHeader.style)
		font = rowHeader.style->value(Value_Font);
	if (!font)
		font = style->font();

	const BFont* itemFont = style->font();

	rowHeader.resizable = false;
	int hintWidth = rowHeader.baseSize.width() + rowHeader.margin.width();
	int hintHeight = rowHeader.spacing;
	int realWidth = rowHeader.baseSize.width();
	int realHeight = rowHeader.spacing;
	for (int r = 0; r < rowHeader.headers.size(); r++)
	{
		IHeader& header = rowHeader.headers[r];
		if (!header.visible)
			continue;
		if (header.dirtyItems && header.policy & Policy_Preferred)
		{
			header.itemsSize.reset();
			for (int c = 0; c < items.column(); c++)
			{
				int width = columnHeader.headers[c].realSize.width();
				BTableItem* item = items[r][c];
				if (!item)
					continue;
				if (item_member(item)->dirtyStyle)
				{
					item_member(item)->dispenseStyles(style);
					item_member(item)->applyStyle(style);
					item_member(item)->dirtyStyle = false;
					item_member(item)->dirty = true;
				}
				if (item_member(item)->dirty)
				{
					item_member(item)->freshSize(itemFont, width);
					item_member(item)->dirty = false;
				}
				int itemHeight = item_member(item)->hintSize.height();
				if (header.itemsSize.height() < itemHeight)
					header.itemsSize.height() = itemHeight;
			}
			header.dirtyItems = false;
		}
		if (header.dirty)
		{
			header.freshSize(rowHeader.style, font);
			header.hintSize += rowHeader.margin.size();
			header.hintSize.limit(rowHeader.minSize, rowHeader.maxSize);
			header.rect.setWidth(0);
			header.dirty = false;
		}
		header.realSize.reset();
		if (header.policy & Policy_Preferred && header.realSize.height() < header.hintSize.height())
			header.realSize.height() = header.hintSize.height();
		if (header.policy & Policy_Fixed && header.realSize.height() < header.fixedSize.height())
			header.realSize.height() = header.fixedSize.height();
		if (header.policy & Policy_Dynamic)
			rowHeader.resizable = true;
		if (hintWidth < header.hintSize.width())
			hintWidth = header.hintSize.width();
		hintHeight += header.hintSize.height() + rowHeader.spacing;
		if (realWidth < header.realSize.width())
			realWidth = header.realSize.width();
		realHeight += header.realSize.height() + rowHeader.spacing;
	}
	rowHeader.hintSize.set(hintWidth, hintHeight);
	rowHeader.realSize.set(realWidth, realHeight);
}

void member_BTableWidget::adjustHeader()
{
	const BStyle* style = boss->realStyle();
	if (columnHeader.dirty)
	{
		freshColumnHeader(style);
		columnHeader.dirty = false;
	}
	if (rowHeader.dirty)
	{
		freshRowHeader(style);
		rowHeader.dirty = false;
	}

	BRect area = boss->area();
	area.setSize(columnHeader.realSize.width(), rowHeader.realSize.height());
	boss->setArea(area);
	area = boss->area();

	if (columnHeader.resizable && area.width() != columnHeader.hintSize.width())
	{
		columnHeader.distributeWidth(area.width());
		columnHeader.realSize.width() = area.width();
	}
	if (rowHeader.resizable && area.height() != rowHeader.hintSize.height())
	{
		rowHeader.distributeHeight(area.height());
		rowHeader.realSize.height() = area.height();
	}

	const BRect& topRect = boss->rect(Part_Top);
	int x = area.left();
	for (int i = 0; i < columnHeader.headers.size(); i++)
	{
		IHeader& header = columnHeader.headers[i];
		if (!header.visible)
			continue;
		header.ia_align = columnHeader.align;
		x += columnHeader.spacing;
		if (header.rect.left() != x || header.rect.width() != header.realSize.width())
		{
			header.rect.left() = x;
			header.rect.right() = x + header.realSize.width();
			header.dirty = true;
		}
		if (header.rect.top() != topRect.top() || header.rect.bottom() != topRect.bottom())
		{
			header.rect.top() = topRect.top();
			header.rect.bottom() = topRect.bottom();
			header.dirty = true;
		}
		if (header.dirty)
		{
			header.crect = header.rect;
			header.crect.left() += columnHeader.margin.left();
			header.crect.top() += columnHeader.margin.top();
			header.crect.right() -= columnHeader.margin.right();
			header.crect.bottom() -= columnHeader.margin.bottom();
			header.adjustRect();
			header.dirty = false;
		}
		x += header.realSize.width();
	}

	const BRect& leftRect = boss->rect(Part_Left);
	int y = area.top();
	for (int i = 0; i < rowHeader.headers.size(); i++)
	{
		IHeader& header = rowHeader.headers[i];
		header.ia_align = rowHeader.align;
		if (!header.visible)
			continue;
		y += rowHeader.spacing;
		if (header.rect.top() != y || header.rect.height() != header.realSize.height())
		{
			header.rect.top() = y;
			header.rect.bottom() = y + header.realSize.height();
			header.dirty = true;
		}
		if (header.rect.left() != leftRect.left() || header.rect.right() != leftRect.right())
		{
			header.rect.left() = leftRect.left();
			header.rect.right() = leftRect.right();
			header.dirty = true;
		}
		if (header.dirty)
		{
			header.crect = header.rect;
			header.crect.left() += rowHeader.margin.left();
			header.crect.top() += rowHeader.margin.top();
			header.crect.right() -= rowHeader.margin.right();
			header.crect.bottom() -= rowHeader.margin.bottom();
			header.adjustRect();
			header.dirty = false;
		}
		y += header.rect.height();
	}

}
void member_BTableWidget::adjustItemRects()
{
	columnHeader.show_begin = -1;
	columnHeader.show_end = -1;
	const BRect& topRect = boss->rect(Part_Top);
	for (int i = 0; i < columnHeader.headers.size(); i++)
	{
		IHeader& header = columnHeader.headers[i];
		if (topRect.left() > header.rect.right())
			continue;
		if (columnHeader.show_begin < 0)
			columnHeader.show_begin = i;
		if (columnHeader.show_begin >= 0)
			columnHeader.show_end = i + 1;
		if (topRect.right() < header.rect.left())
			break;
	}

	rowHeader.show_begin = -1;
	rowHeader.show_end = -1;
	const BRect& leftRect = boss->rect(Part_Left);
	for (int i = 0; i < rowHeader.headers.size(); i++)
	{
		IHeader& header = rowHeader.headers[i];
		if (leftRect.top() > header.rect.bottom())
			continue;
		if (rowHeader.show_begin < 0)
			rowHeader.show_begin = i;
		if (rowHeader.show_begin >= 0)
			rowHeader.show_end = i + 1;
		if (leftRect.bottom() < header.rect.top())
			break;
	}

	const BStyle* style = boss->realStyle();
	const BFont* itemFont = style->font();
	for (int r = 0; r < rowHeader.headers.size(); r++)
	{
		int y = rowHeader.headers[r].rect.y();
		int height = rowHeader.headers[r].rect.height();
		for (int c = 0; c < columnHeader.headers.size(); c++)
		{
			BTableItem* item = items[r][c];
			if (item == 0)
				continue;
			int x = columnHeader.headers[c].rect.x();
			int width = columnHeader.headers[c].rect.width();
			if (item_member(item)->dirtyStyle)
			{
				item_member(item)->dispenseStyles(style);
				item_member(item)->applyStyle(style);
				item_member(item)->dirtyStyle = false;
				item_member(item)->dirty = true;
			}
			if (item_member(item)->dirty)
			{
				item_member(item)->freshSize(itemFont, width);
				item_member(item)->dirty = false;
			}
			item_member(item)->setPos(x, y);
			item_member(item)->setSize(width, height);
		}
	}
}
bool member_BTableWidget::selectItemsInside()
{
	BTableItemArray collectedItems;
	BRange rowRange = rowHeader.yrange(selectRect.top(), selectRect.bottom());
	if (selectMode & Select_Rows)
	{
		for (int r = rowRange.min(); r < rowRange.max(); r++)
		{
			if (BTableItem* item = items(r, 0))
				collectedItems.append(item);
		}
	}
	else
	{
		BRange columnRange = columnHeader.xrange(selectRect.left(), selectRect.right());
		for (int r = rowRange.min(); r < rowRange.max(); r++)
		{
			for (int c = columnRange.min(); c < columnRange.max(); c++)
			{
				if (BTableItem* item = items(r, c))
					collectedItems.append(item);
			}
		}
	}
	bool selectionChanged = false;
	for (int i = 0; i < selectedItems.size(); i++)
	{
		BTableItem* temp = selectedItems[i];
		if (!collectedItems.contain(temp))
		{
			selectedItems.remove(temp);
			item_member(temp)->selected = false;
			emit(Signal_ItemSelected, temp);
			selectionChanged = true;
		}
	}
	for (int i = 0; i < collectedItems.size(); i++)
	{
		BTableItem* temp = collectedItems[i];
		if (!selectedItems.contain(temp))
		{
			selectedItems.append(temp);
			item_member(temp)->selected = true;
			emit(Signal_ItemSelected, temp);
			selectionChanged = true;
		}
	}
	selectedItems = collectedItems;
	return selectionChanged;
}
